﻿using System;
using System.Collections;
using Dragon.Library.Framework.Entity.Search.Page;
using Dragon.Library.Framework.Entity.Search.Query;
using Dragon.Library.Utility.NHibernate;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Type;

namespace Dragon.Library.Framework.Dao
{
    public partial class BaseDao<T, TKeyT>
    {
        public PageResult<T> GetPageList(PageSearch pageSearch, QueryLevel queryLevel)
        {
            switch (queryLevel)
            {
                case QueryLevel.Advanced:
                    return GetPageListAdvanced(pageSearch);
                case QueryLevel.General:
                    return GetPageList(pageSearch);
            }
            return new PageResult<T>();
        }

        private PageResult<T> GetPageList(PageSearch pageSearch)
        {
            var pageResult = new PageResult<T>();

            var session = SessionFactory.GetSession(SessionKey, ConfigFileName);
            var criteria = session.CreateCriteria(typeof(T));

            criteria = CriteriaQueryBuilder(criteria, pageSearch.Search);

            for (var i = 0; i < _pageQueryKeies.Length; i++)
            {
                criteria = criteria.SetProjection(Projections.GroupProperty(_pageQueryKeies[i]));
            }
            criteria = criteria.SetProjection(Projections.Count(_pageQueryKeies[0]));

            pageResult.RecordCount = Convert.ToInt32(criteria.UniqueResult());
            criteria = session.CreateCriteria(typeof(T));

            criteria.SetFirstResult((pageSearch.PageIndex - 1) * pageSearch.PageSize)
                    .SetMaxResults(pageSearch.PageSize);
            criteria = CriteriaOrderBuilder(criteria, pageSearch.Search);
            criteria = CriteriaQueryBuilder(criteria, pageSearch.Search);

            pageResult.Data = criteria.List<T>();
            pageResult.PageSize = pageSearch.PageSize;
            return pageResult;
        }

        public PageResult<T> GetPageList(PageSearchString pageSearch)
        {
            var pageResult = new PageResult<T>();
            IQuery query = Session.CreateQuery(pageSearch.CountString.SearchString);
            foreach (var parameter in pageSearch.CountString.Parameters)
            {
                query.SetParameter(parameter.Key, parameter.Value);
            }
            pageResult.RecordCount = Convert.ToInt32(query.UniqueResult());
            query = Session.CreateQuery(pageSearch.SearchString.SearchString);
            foreach (var parameter in pageSearch.SearchString.Parameters)
            {
                query.SetParameter(parameter.Key, parameter.Value);
            }
            query.SetFirstResult((pageSearch.PageIndex - 1) * pageSearch.PageSize)
                    .SetMaxResults(pageSearch.PageSize);
            pageResult.Data = query.List<T>();
            pageResult.PageSize = pageSearch.PageSize;
            return pageResult;
        }

        private static ICriteria CriteriaQueryBuilder(ICriteria criteria, QueryGroup pageSearch)
        {
            if (pageSearch != null && pageSearch.QuerySearch.Count > 0)
            {
                for (var i = 0; i < pageSearch.QuerySearch.Count; i++)
                {
                    criteria.Add(CriteriaQueryConditionBuilder(pageSearch.QuerySearch[i]));
                }
            }
            return criteria;
        }

        private static ICriteria CriteriaOrderBuilder(ICriteria criteria, QueryGroup pageSearch)
        {
            if (pageSearch != null)
            {
                if (!string.IsNullOrEmpty(pageSearch.OrderProperty))
                {
                    if (pageSearch.SortOrder.ToLower() == "desc")
                        criteria.AddOrder(Order.Desc(pageSearch.OrderProperty));
                    else
                        criteria.AddOrder(Order.Asc(pageSearch.OrderProperty));
                }
            }
            return criteria;
        }

        private static ICriterion CriteriaQueryConditionBuilder(QuerySearch querySearch)
        {
            Type dt = typeof(T);
            IType t = NHibernate.Util.ReflectHelper.ReflectedPropertyType(dt, querySearch.PropertyPatch, "");
            if (t != null)
                dt = t.ReturnedClass;
            switch (querySearch.QueryCondition)
            {
                case QueryCondition.Equal:
                    return Restrictions.Eq(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt));
                case QueryCondition.GreaterThan:
                    return Restrictions.Gt(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt));
                case QueryCondition.GreaterThanOrEqual:
                    return Restrictions.Ge(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt));
                case QueryCondition.LeftLike:
                    return Restrictions.Like(querySearch.PropertyPatch, "%" + querySearch.QueryValue);
                case QueryCondition.LessThan:
                    return Restrictions.Lt(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt));
                case QueryCondition.LessThanOrEqual:
                    return Restrictions.Le(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt));
                case QueryCondition.Like:
                    return Restrictions.Like(querySearch.PropertyPatch, "%" + querySearch.QueryValue + "%");
                case QueryCondition.RightLike:
                    return Restrictions.Like(querySearch.PropertyPatch, querySearch.QueryValue + "%");
                case QueryCondition.IsNull:
                    return Restrictions.IsNull(querySearch.PropertyPatch);
                case QueryCondition.NotEqual:
                    return Restrictions.Not(Restrictions.Eq(querySearch.PropertyPatch, Convert.ChangeType(querySearch.QueryValue, dt)));
                case QueryCondition.In:
                    return Restrictions.In(querySearch.PropertyPatch, (ICollection)querySearch.QueryValue);
            }
            return null;
        }

        private PageResult<T> GetPageListAdvanced(PageSearch pageSearch)
        {
            PageResult<T> pageResult = new PageResult<T>();

            ISession session = SessionFactory.GetSession(SessionKey, ConfigFileName);
            ICriteria criteria = session.CreateCriteria(typeof(T));

            criteria = CriteriaQueryBuilderAdvanced(criteria, pageSearch.Search);

            for (int i = 0; i < _pageQueryKeies.Length; i++)
            {
                criteria = criteria.SetProjection(Projections.GroupProperty(_pageQueryKeies[i]));
            }
            criteria = criteria.SetProjection(Projections.Count(_pageQueryKeies[0]));

            pageResult.RecordCount = Convert.ToInt32(criteria.UniqueResult());
            criteria = session.CreateCriteria(typeof(T));

            criteria.SetFirstResult((pageSearch.PageIndex - 1) * pageSearch.PageSize)
                    .SetMaxResults(pageSearch.PageSize);
            criteria = CriteriaOrderBuilderAdvanced(criteria, pageSearch.Search);
            criteria = CriteriaQueryBuilderAdvanced(criteria, pageSearch.Search);

            pageResult.Data = criteria.List<T>();
            pageResult.PageSize = pageSearch.PageSize;
            return pageResult;
        }

        private static ICriteria CriteriaQueryBuilderAdvanced(ICriteria criteria, QueryGroup pageSearch)
        {
            if (pageSearch != null && pageSearch.QuerySearch.Count > 0)
            {

                for (int i = 0; i < pageSearch.QuerySearch.Count; i++)
                {
                    CriteriaQueryConditionBuilderAdvanced(criteria, pageSearch.QuerySearch[i]);
                }
            }
            return criteria;
        }

        private static ICriteria CriteriaOrderBuilderAdvanced(ICriteria criteria, QueryGroup pageSearch)
        {
            if (pageSearch != null)
            {
                if (!string.IsNullOrEmpty(pageSearch.OrderProperty))
                {
                    if (pageSearch.SortOrder.ToLower() == "desc")
                        criteria.AddOrder(NHibernate.Criterion.Order.Desc(pageSearch.OrderProperty));
                    else
                        criteria.AddOrder(NHibernate.Criterion.Order.Asc(pageSearch.OrderProperty));
                }
            }
            return criteria;
        }

        private static ICriteria CriteriaQueryConditionBuilderAdvanced(ICriteria criteria, QuerySearch querySearch)
        {
            string[] p = querySearch.PropertyPatch.Split('.');
            ICriteria c = criteria;

            Type dt = typeof(T);
            for (int i = 0; i < p.Length; i++)
            {
                if (p.Length > 1 && i == 0)
                    c = criteria.CreateCriteria(p[0]);
                //criteria =
                //    criteria.CreateCriteria(
                //        querySearch.PropertyPatch.Substring(querySearch.PropertyPatch.IndexOf('.')+1));
                dt = NHibernate.Util.ReflectHelper.ReflectedPropertyType(dt, p[i], "").ReturnedClass;
                if (i == p.Length - 1)
                {
                    string path = querySearch.PropertyPatch.Substring(querySearch.PropertyPatch.IndexOf('.') + 1);
                    switch (querySearch.QueryCondition)
                    {
                        case QueryCondition.Equal:
                            c.Add(Restrictions.Eq(path, Convert.ChangeType(querySearch.QueryValue, dt)));
                            break;
                        case QueryCondition.GreaterThan:
                            c.Add(Restrictions.Gt(path, Convert.ChangeType(querySearch.QueryValue, dt)));
                            break;
                        case QueryCondition.GreaterThanOrEqual:
                            c.Add(Restrictions.Ge(path, Convert.ChangeType(querySearch.QueryValue, dt)));
                            break;
                        case QueryCondition.LeftLike:
                            c.Add(Restrictions.Like(path, "%" + querySearch.QueryValue));
                            break;
                        case QueryCondition.LessThan:
                            c.Add(Restrictions.Lt(path, Convert.ChangeType(querySearch.QueryValue, dt)));
                            break;
                        case QueryCondition.LessThanOrEqual:
                            c.Add(Restrictions.Le(path, Convert.ChangeType(querySearch.QueryValue, dt)));
                            break;
                        case QueryCondition.Like:
                            c.Add(Restrictions.Like(path, "%" + querySearch.QueryValue + "%"));
                            break;
                        case QueryCondition.RightLike:
                            c.Add(Restrictions.Like(path, querySearch.QueryValue + "%"));
                            break;
                        case QueryCondition.IsNull:
                            c.Add(Restrictions.IsNull(path));
                            break;
                        case QueryCondition.NotEqual:
                            c.Add(Restrictions.Not(Restrictions.Eq(path, Convert.ChangeType(querySearch.QueryValue, dt))));
                            break;
                    }
                }
            }
            return criteria;
        }
    }
}
